/*
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 * 
 * The Original Code is iSQL-Viewer, A Mutli-Platform Database Tool.
 *
 * The Initial Developer of the Original Code is iSQL-Viewer, A Mutli-Platform Database Tool.
 * Portions created by Mark A. Kobold are Copyright (C) 2000-2007. All Rights Reserved.
 *
 * Contributor(s): 
 *  Mark A. Kobold [mkobold <at> isqlviewer <dot> com].
 *  
 * If you didn't download this code from the following link, you should check
 * if you aren't using an obsolete version: http://www.isqlviewer.com
 */
package org.isqlviewer.swing;

import java.awt.Component;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.text.DateFormat;
import java.util.Date;

import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.text.BadLocationException;
import javax.swing.text.JTextComponent;

import org.apache.log4j.Logger;
import org.isqlviewer.util.IsqlToolkit;
import org.isqlviewer.util.LocalMessages;
import org.isqlviewer.util.StringUtilities;

/**
 * @author Mark A. Kobold &lt;mkobold at isqlviewer dot com&gt;
 * @version 1.0
 */
public class JTextPopup extends JPopupMenu implements MouseListener, ActionListener {

    private static final long serialVersionUID = -4349534125094508822L;
    private static final String BUNDLE_IDENTIFIER = "org.isqlviewer.swing.ResourceBundle";

    private static final int ACTION_SELECT_ALL = 1;
    private static final int ACTION_CLEAR = 2;
    private static final int ACTION_PASTE = 3;
    private static final int ACTION_COPY = 4;
    private static final int ACTION_DELETE = 5;
    private static final int ACTION_CUT = 6;
    private static final int ACTION_LONG_TO_DATE = 7;
    private static final int ACTION_B64_DECODE = 8;
    private static final int ACTION_B64_ENCODE = 9;
    private static final int ACTION_NATIVE_TO_ASCII = 10;
    private static final int ACTION_ASCII_TO_NATIVE = 11;

    private LocalMessages messages = new LocalMessages(BUNDLE_IDENTIFIER);
    private JMenuItem cut = new JMenuItem(messages.format("JTextPopup.cut.action"));
    private JMenuItem delete = new JMenuItem(messages.format("JTextPopup.delete.action"));
    private JMenuItem copy = new JMenuItem(messages.format("JTextPopup.copy.action"));
    private JMenuItem paste = new JMenuItem(messages.format("JTextPopup.paste.action"));
    private JMenuItem clear = new JMenuItem(messages.format("JTextPopup.clear_all.action"));
    private JMenuItem selectAll = new JMenuItem(messages.format("JTextPopup.select_all.action"));

    private JMenuItem asciiToNative = new JMenuItem(messages.format("JTextPopup.unicode_decode.action"));
    private JMenuItem nativeToAscii = new JMenuItem(messages.format("JTextPopup.unicode_encode.action"));
    private JMenuItem encodeBase64 = new JMenuItem(messages.format("JTextPopup.base64_encode.action"));
    private JMenuItem decodeBase64 = new JMenuItem(messages.format("JTextPopup.base64_decode.action"));
    private JMenuItem longToDate = new JMenuItem(messages.format("JTextPopup.millisecond_date_decode.action"));

    private JTextComponent textComponent;
    private DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL);
    private Logger logger = IsqlToolkit.getApplicationLogger();

    public JTextPopup() {

        JMenu tools = new JMenu(messages.format("JTextPopup.text_tools.title"));
        tools.add(decodeBase64);
        tools.add(encodeBase64);
        tools.add(asciiToNative);
        tools.add(nativeToAscii);
        tools.addSeparator();
        tools.add(longToDate);

        add(cut);
        add(copy);
        add(paste);
        add(delete);
        addSeparator();
        add(tools);
        add(clear);
        add(selectAll);

        cut.setActionCommand(Integer.toString(ACTION_CUT));
        delete.setActionCommand(Integer.toString(ACTION_DELETE));
        copy.setActionCommand(Integer.toString(ACTION_COPY));
        paste.setActionCommand(Integer.toString(ACTION_PASTE));
        clear.setActionCommand(Integer.toString(ACTION_CLEAR));
        selectAll.setActionCommand(Integer.toString(ACTION_SELECT_ALL));

        asciiToNative.setActionCommand(Integer.toString(ACTION_ASCII_TO_NATIVE));
        nativeToAscii.setActionCommand(Integer.toString(ACTION_NATIVE_TO_ASCII));
        encodeBase64.setActionCommand(Integer.toString(ACTION_B64_ENCODE));
        decodeBase64.setActionCommand(Integer.toString(ACTION_B64_DECODE));
        longToDate.setActionCommand(Integer.toString(ACTION_LONG_TO_DATE));

        if (!SwingUtilities.isMacOS()) {
            selectAll.setIcon(SwingUtilities.loadIconResource("spacer", 16));
            cut.setIcon(SwingUtilities.loadIconResource("edit_cut", 16));
            copy.setIcon(SwingUtilities.loadIconResource("edit_copy", 16));
            paste.setIcon(SwingUtilities.loadIconResource("edit_paste", 16));
            clear.setIcon(SwingUtilities.loadIconResource("edit_clear", 16));
            delete.setIcon(SwingUtilities.loadIconResource("edit_delete", 16));
            tools.setIcon(SwingUtilities.loadIconResource("spacer", 16));
        }

        paste.setAccelerator(SwingUtilities.createKeyStroke(KeyEvent.VK_V, SwingUtilities.MENU_SHORTCUT_MASK));
        copy.setAccelerator(SwingUtilities.createKeyStroke(KeyEvent.VK_C, SwingUtilities.MENU_SHORTCUT_MASK));
        selectAll.setAccelerator(SwingUtilities.createKeyStroke(KeyEvent.VK_A, SwingUtilities.MENU_SHORTCUT_MASK));
        cut.setAccelerator(SwingUtilities.createKeyStroke(KeyEvent.VK_X, SwingUtilities.MENU_SHORTCUT_MASK));

        selectAll.addActionListener(this);
        cut.addActionListener(this);
        copy.addActionListener(this);
        paste.addActionListener(this);
        clear.addActionListener(this);
        decodeBase64.addActionListener(this);
        encodeBase64.addActionListener(this);
        asciiToNative.addActionListener(this);
        nativeToAscii.addActionListener(this);
        longToDate.addActionListener(this);
    }

    public final void removeJTextComponent(JTextComponent jtc) {

        jtc.removeMouseListener(this);
    }

    public final void addJTextComponent(JTextComponent jtc) {

        jtc.addMouseListener(this);
    }

    public void actionPerformed(ActionEvent actionEvent) {

        if (textComponent == null) {
            return;
        }
        String actionCommand = actionEvent.getActionCommand();
        int action = -1;
        try {
            action = Integer.parseInt(actionCommand);
        } catch (Exception ignored) {
        }

        String text = textComponent.getText();
        String selectedText = textComponent.getSelectedText();
        int offset = textComponent.getSelectionStart();
        boolean selection = selectedText != null;

        switch (action) {
            case ACTION_SELECT_ALL :
                textComponent.selectAll();
                break;
            case ACTION_CLEAR :
                if (textComponent.isEnabled()) {
                    textComponent.setText("");
                    textComponent.setCaretPosition(0);
                }
                break;
            case ACTION_PASTE :
                textComponent.paste();
                break;
            case ACTION_COPY :
                textComponent.copy();
                break;
            case ACTION_DELETE :
                if (selectedText != null && textComponent.isEditable()) {
                    try {
                        textComponent.getDocument().remove(offset, selectedText.length());
                    } catch (BadLocationException ignored) {
                        logger.debug("textComponent.remove(int,int)", ignored);
                    }
                }
                break;
            case ACTION_CUT :
                textComponent.copy();
                if (textComponent.isEditable()) {
                    textComponent.cut();
                }
                break;
            case ACTION_LONG_TO_DATE :
                String dateString = null;
                long milliseconds = -1;
                try {
                    if (selection) {
                        Long.parseLong(selectedText);
                    } else {
                        Long.parseLong(text);
                    }
                } catch (NumberFormatException ignored) {
                    SwingUtilities.beep();
                    return;
                }

                Date date = new Date(milliseconds);
                dateString = dateFormat.format(date);
                if (textComponent.isEditable()) {
                    if (selection) {
                        textComponent.replaceSelection(dateString);
                    } else {
                        textComponent.setText(dateString);
                    }
                } else {
                    Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
                    StringSelection ss = new StringSelection(dateString);
                    cb.setContents(ss, ss);
                    logger.info(messages.format("JTextPopup.content_copied_to_clipboard"));
                }
                break;
            case ACTION_B64_DECODE :
                if (textComponent.isEditable()) {
                    if (selection) {
                        selectedText = StringUtilities.decodeBase64(selectedText);
                        textComponent.replaceSelection(selectedText);
                    } else {
                        text = StringUtilities.decodeBase64(text);
                        textComponent.setText(text);
                    }
                } else {
                    Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
                    StringSelection ss = null;
                    if (selection) {
                        ss = new StringSelection(StringUtilities.decodeBase64(selectedText));
                    } else {
                        ss = new StringSelection(StringUtilities.decodeBase64(text));
                    }
                    cb.setContents(ss, ss);
                    logger.info(messages.format("JTextPopup.content_copied_to_clipboard"));
                }
                break;
            case ACTION_B64_ENCODE :
                if (textComponent.isEditable()) {
                    if (selection) {
                        selectedText = StringUtilities.encodeBase64(selectedText);
                        textComponent.replaceSelection(selectedText);
                    } else {
                        text = StringUtilities.encodeBase64(text);
                        textComponent.setText(text);
                    }
                } else {
                    Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
                    StringSelection ss = null;
                    if (selection) {
                        ss = new StringSelection(StringUtilities.encodeBase64(selectedText));
                    } else {
                        ss = new StringSelection(StringUtilities.encodeBase64(text));
                    }
                    cb.setContents(ss, ss);
                    logger.info(messages.format("JTextPopup.content_copied_to_clipboard"));
                }
                break;
            case ACTION_NATIVE_TO_ASCII :
                if (textComponent.isEditable()) {
                    if (selection) {
                        selectedText = StringUtilities.decodeASCII(selectedText);
                        textComponent.replaceSelection(selectedText);
                    } else {
                        text = StringUtilities.decodeASCII(text);
                        textComponent.setText(text);
                    }
                } else {
                    Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
                    StringSelection ss = null;
                    if (selection) {
                        ss = new StringSelection(StringUtilities.decodeASCII(selectedText));
                    } else {
                        ss = new StringSelection(StringUtilities.decodeASCII(text));
                    }
                    cb.setContents(ss, ss);
                    logger.info(messages.format("JTextPopup.content_copied_to_clipboard"));
                }
                break;
            case ACTION_ASCII_TO_NATIVE :
                if (textComponent.isEditable()) {
                    if (selection) {
                        selectedText = StringUtilities.encodeASCII(selectedText);
                        textComponent.replaceSelection(selectedText);
                    } else {
                        text = StringUtilities.encodeASCII(text);
                        textComponent.setText(text);
                    }
                } else {
                    Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
                    StringSelection ss = null;
                    if (selection) {
                        ss = new StringSelection(StringUtilities.encodeASCII(selectedText));
                    } else {
                        ss = new StringSelection(StringUtilities.encodeASCII(text));
                    }
                    cb.setContents(ss, ss);
                }
                break;
            default :
                break;
        }
    }

    @Override
    public void show(Component com, int x, int y) {

        Component deepest = javax.swing.SwingUtilities.getDeepestComponentAt(com, x, y);
        if (deepest instanceof JTextComponent && com.isEnabled()) {
            textComponent = (JTextComponent) deepest;

            cut.setEnabled(textComponent.isEditable());
            paste.setEnabled(textComponent.isEditable());
            delete.setEnabled(textComponent.isEditable());
            super.show(com, x, y);
        }
    }

    public void mouseReleased(MouseEvent e) {

        doPopup(e);
    }

    public void mouseClicked(MouseEvent e) {

        doPopup(e);
    }

    public void mousePressed(MouseEvent e) {

        doPopup(e);
    }

    public void mouseEntered(MouseEvent e) {

    }

    public void mouseExited(MouseEvent e) {

    }

    private boolean doPopup(MouseEvent e) {

        if (e.isPopupTrigger()) {
            show(e.getComponent(), e.getX(), e.getY());
            e.consume();
            return true;
        }
        return false;
    }
}